/*
 * Copyright (c) 2006-2018, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2019-01-08     Ghazigq      first version
 */

#include <board.h>
#include <rthw.h>

#ifdef BSP_USING_CAMERA
#include <camera_port.h>
#include <ov2640_cfg.h>
#include <rtdevice.h>

////#define DRV_DEBUG
//#define LOG_TAG             "drv.camera"
//#define DBG_LVL             DBG_LOG
//#include <drv_log.h>
#define DBG_TAG             "drv.camera"
#define DBG_LVL             DBG_INFO
#include <rtdbg.h>

#ifdef BSP_USING_OV2640
#include "ov2640.h"
#endif

struct drv_camera_device
{
    struct rt_device parent;

    struct rt_semaphore camera_sem;
    struct rt_mutex camera_lock;

    struct rt_device_graphic_info camera_info;

    struct rt_i2c_bus_device  * i2c_device;

    DCMI_HandleTypeDef dcmi;

    DMA_HandleTypeDef hdma_dcmi;
};

struct drv_camera_device _camera = {0};

#ifdef CAMERA_RST_GPIO_NUM
static void rst_camera(void)
{
    rt_pin_mode(CAMERA_RST_GPIO_NUM, PIN_MODE_OUTPUT);

    rt_pin_write(CAMERA_RST_GPIO_NUM, PIN_LOW);
    rt_thread_mdelay(10);
    rt_pin_write(CAMERA_RST_GPIO_NUM, PIN_HIGH);
    rt_thread_mdelay(10);
}
#endif
#ifdef CAMERA_EN_GPIO_NUM
static void en_camera(void)
{
    rt_pin_mode(CAMERA_EN_GPIO_NUM, PIN_MODE_OUTPUT);
    rt_pin_write(CAMERA_EN_GPIO_NUM, PIN_HIGH);
}
#endif
#ifdef  CAMERA_PWND_GPIO_NUM
static void en_camera_pwnd(void)
{
    rt_pin_mode(CAMERA_PWND_GPIO_NUM, PIN_MODE_OUTPUT);
    rt_pin_write(CAMERA_PWND_GPIO_NUM, PIN_LOW);
}
#endif

static uint8_t camera_read_reg(uint8_t reg)
{
    struct rt_i2c_msg msgs;
    uint8_t buff = 0;

    buff = reg;

    msgs.addr = CAMERA_IIC_BUS_ADDR;     /* 从机地址 */
    msgs.flags = RT_I2C_WR;     /* 写标志 */
    msgs.buf = &buff;             /* 读写数据缓冲区指针　*/
    msgs.len = 1;              /* 读写数据字节数 */

    /* 调用I2C设备接口传输数据 */
    rt_i2c_transfer(_camera.i2c_device, &msgs, 1);

    buff = 0;

    msgs.flags = RT_I2C_RD;     /* 读标志 */
    msgs.buf = &buff;             /* 读写数据缓冲区指针　*/
    msgs.len = 1;              /* 读写数据字节数 */
    rt_i2c_transfer(_camera.i2c_device, &msgs, 1);

    return buff;
}

static void camera_write_reg(uint8_t reg, uint8_t dat)
{
    struct rt_i2c_msg msgs;
    uint8_t buff[2] = {0};

    buff[0] = reg;
    buff[1] = dat;

    msgs.addr = CAMERA_IIC_BUS_ADDR;     /* 从机地址 */
    msgs.flags = RT_I2C_WR;     /* 读标志 */
    msgs.buf = buff;             /* 读写数据缓冲区指针　*/
    msgs.len = 2;              /* 读写数据字节数 */

    /* 调用I2C设备接口传输数据 */
    rt_i2c_transfer(_camera.i2c_device, &msgs, 1);
}

static void camera_set_outsize(uint16_t width,uint16_t height)
{
    uint16_t outh;
    uint16_t outw;
    uint8_t temp;
    if(width % 4)
    {
        LOG_E("camera width can't be divisible by 4");
        return;
    }
    if(height % 4)
    {
        LOG_E("camera height can't be divisible by 4");
        return;
    }
    outw = width / 4;
    outh = height / 4;
    camera_write_reg(0XFF,0X00);
    camera_write_reg(0XE0,0X04);
    camera_write_reg(0X5A,outw & 0XFF);        //设置OUTW的低八位
    camera_write_reg(0X5B,outh & 0XFF);        //设置OUTH的低八位
    temp = (outw >> 8) & 0X03;
    temp |= (outh >> 6) & 0X04;
    camera_write_reg(0X5C,temp);             //设置OUTH/OUTW的高位
    camera_write_reg(0XE0,0X00);
}

static rt_err_t drv_camera_init(struct rt_device *device)
{
    struct drv_camera_device *camera = (struct drv_camera_device*)(device);
    /* nothing, right now */
    camera = camera;
    return RT_EOK;
}

static rt_size_t drv_camera_read(struct rt_device *device, rt_off_t pos, void *buffer, rt_size_t size)
{
    rt_mutex_take(&_camera.camera_lock, RT_WAITING_FOREVER);
    rt_memcpy(buffer, &_camera.camera_info.framebuffer[pos], size);
    rt_mutex_release(&_camera.camera_lock);
    return size;
}

static rt_err_t drv_camera_control(struct rt_device *device, int cmd, void *args)
{
    struct drv_camera_device *camera = (struct drv_camera_device*)(device);
    struct rt_device_rect_info *rect_info = (struct rt_device_rect_info *)args;

    RT_ASSERT(rect_info != RT_NULL);

    switch (cmd)
    {
    case RTGRAPHIC_CTRL_GET_INFO:
    {
        struct rt_device_graphic_info *info = (struct rt_device_graphic_info *)args;

        RT_ASSERT(info != RT_NULL);
        info->pixel_format  = camera->camera_info.pixel_format;
        info->bits_per_pixel= camera->camera_info.bits_per_pixel;
        info->width         = camera->camera_info.width;
        info->height        = camera->camera_info.height;
        info->framebuffer   = camera->camera_info.framebuffer;
    }
    break;
    }

    return RT_EOK;
}

rt_err_t stm32_camera_init(struct drv_camera_device *camera)
{
    uint16_t value = 0;

    //init ov2640
    camera_write_reg(OV2640_DSP_RA_DLMT, 0x01);  //操作sensor寄存器
    camera_write_reg(OV2640_SENSOR_COM7, 0x80);  //软复位OV2640
    rt_thread_mdelay(50);
    value = camera_read_reg(OV2640_SENSOR_MIDH) << 8 | camera_read_reg(OV2640_SENSOR_MIDL);
    if(value != OV2640_MID)    //读取厂家MID
    {
        LOG_E("MID error:0x%04x",value);
        goto _exit;
    }
    value = camera_read_reg(OV2640_SENSOR_PIDH) << 8 | camera_read_reg(OV2640_SENSOR_PIDL);
    if(value != OV2640_PID)    //读取厂家PID
    {
        LOG_E("PID(0x%04x) error",value);
        goto _exit;
    }
    //初始化 OV2640,采用SXGA分辨率(1600*1200)
    for(uint16_t i = 0;i < sizeof(ov2640_sxga_init_reg_tbl) / 2;i ++)
    {
        camera_write_reg(ov2640_sxga_init_reg_tbl[i][0],ov2640_sxga_init_reg_tbl[i][1]);
    }

    rt_thread_mdelay(50);
    //设置数据格式
    if(_camera.camera_info.pixel_format == RTGRAPHIC_PIXEL_FORMAT_RGB565)
    {
        for(uint16_t i = 0;i < (sizeof(ov2640_rgb565_reg_tbl) / 2);i ++)
            camera_write_reg(ov2640_rgb565_reg_tbl[i][0],ov2640_rgb565_reg_tbl[i][1]);
    }
    else {
        LOG_E("pixel_format error!");
        goto _exit;
    }
    //设置白平衡
    camera_write_reg(0XFF,0X00);
    camera_write_reg(0XC7,0X10);//AWB ON
    //设置色彩饱和度
    value = 3;
    value = ((value + 2) << 4) | 0X08;
    camera_write_reg(0XFF,0X00);
    camera_write_reg(0X7C,0X00);
    camera_write_reg(0X7D,0X02);
    camera_write_reg(0X7C,0X03);
    camera_write_reg(0X7D,value);
    camera_write_reg(0X7D,value);
    //设置亮度
    value = 4;
    camera_write_reg(0xff, 0x00);
    camera_write_reg(0x7c, 0x00);
    camera_write_reg(0x7d, 0x04);
    camera_write_reg(0x7c, 0x09);
    camera_write_reg(0x7d, value << 4);
    camera_write_reg(0x7d, 0x00);
    //设置对比度
    camera_write_reg(0xff,0x00);
    camera_write_reg(0x7c,0x00);
    camera_write_reg(0x7d,0x04);
    camera_write_reg(0x7c,0x07);
    camera_write_reg(0x7d,0x20);
    camera_write_reg(0x7d,0X24);//
    camera_write_reg(0x7d,0X16);//
    camera_write_reg(0x7d,0x06);

    //init dcmi
    _camera.dcmi.Instance = DCMI;
    _camera.dcmi.Init.SynchroMode = DCMI_SYNCHRO_HARDWARE;
    _camera.dcmi.Init.PCKPolarity = DCMI_PCKPOLARITY_RISING;
    _camera.dcmi.Init.VSPolarity = DCMI_VSPOLARITY_LOW;
    _camera.dcmi.Init.HSPolarity = DCMI_HSPOLARITY_LOW;
    _camera.dcmi.Init.CaptureRate = DCMI_CR_ALL_FRAME;
    _camera.dcmi.Init.ExtendedDataMode = DCMI_EXTEND_DATA_8B;
    _camera.dcmi.Init.JPEGMode = DCMI_JPEG_DISABLE;
    _camera.dcmi.Init.ByteSelectMode = DCMI_BSM_ALL;
    _camera.dcmi.Init.ByteSelectStart = DCMI_OEBS_ODD;
    _camera.dcmi.Init.LineSelectMode = DCMI_LSM_ALL;
    _camera.dcmi.Init.LineSelectStart = DCMI_OELS_ODD;
    if (HAL_DCMI_Init(&_camera.dcmi) != HAL_OK)
    {
        LOG_E("dcmi init failed");
        goto _exit;
    }
    HAL_NVIC_SetPriority(DCMI_IRQn, 0, 1);
    HAL_NVIC_EnableIRQ(DCMI_IRQn);

    DCMI->IER=0x0;
//    __HAL_DCMI_DISABLE_IT(&_camera.dcmi,DCMI_IT_LINE|DCMI_IT_VSYNC|DCMI_IT_ERR|DCMI_IT_OVR);
    __HAL_DCMI_ENABLE_IT(&_camera.dcmi,DCMI_IT_FRAME);      //使能帧中断
    __HAL_DCMI_ENABLE(&_camera.dcmi);                       //使能DCMI

    //init dma
    __HAL_RCC_DMA1_CLK_ENABLE();                                    //使能DMA1时钟
    __HAL_LINKDMA(&_camera.dcmi,DMA_Handle,_camera.hdma_dcmi);        //将DMA与DCMI联系起来
    __HAL_DMA_DISABLE_IT(&_camera.hdma_dcmi,DMA_IT_TC);               //先关闭DMA传输完成中断(否则在使用MCU屏的时候会出现花屏的情况)

    _camera.hdma_dcmi.Instance = DMA1_Stream1;
    _camera.hdma_dcmi.Init.Request = DMA_REQUEST_DCMI;
    _camera.hdma_dcmi.Init.Direction = DMA_PERIPH_TO_MEMORY;
    _camera.hdma_dcmi.Init.PeriphInc = DMA_PINC_DISABLE;
    _camera.hdma_dcmi.Init.MemInc = DMA_MINC_ENABLE;
    _camera.hdma_dcmi.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
    _camera.hdma_dcmi.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
    _camera.hdma_dcmi.Init.Mode = DMA_CIRCULAR;
    _camera.hdma_dcmi.Init.Priority = DMA_PRIORITY_HIGH;
    _camera.hdma_dcmi.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
    _camera.hdma_dcmi.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_HALFFULL;
    _camera.hdma_dcmi.Init.MemBurst = DMA_MBURST_SINGLE;
    _camera.hdma_dcmi.Init.PeriphBurst = DMA_PBURST_SINGLE;
    HAL_DMA_DeInit(&_camera.hdma_dcmi);                               //先清除以前的设置
    HAL_DMA_Init(&_camera.hdma_dcmi);                                 //初始化DMA

    //在开启DMA之前先使用__HAL_UNLOCK()解锁一次DMA,因为HAL_DMA_Statrt()HAL_DMAEx_MultiBufferStart()
    //这两个函数一开始要先使用__HAL_LOCK()锁定DMA,而函数__HAL_LOCK()会判断当前的DMA状态是否为锁定状态，如果是
    //锁定状态的话就直接返回HAL_BUSY，这样会导致函数HAL_DMA_Statrt()和HAL_DMAEx_MultiBufferStart()后续的DMA配置
    //程序直接被跳过！DMA也就不能正常工作，为了避免这种现象，所以在启动DMA之前先调用__HAL_UNLOC()先解锁一次DMA。
    __HAL_UNLOCK(&_camera.hdma_dcmi);
//    HAL_DMA_Start_IT
    HAL_DMA_Start(&_camera.hdma_dcmi,(uint32_t)&DCMI->DR,(uint32_t)(&_camera.camera_info.framebuffer[0]),CAMERA_BUF_SIZE / 2);
//    HAL_DMAEx_MultiBufferStart(&_camera.hdma_dcmi,(uint32_t)(&DCMI->DR),(uint32_t)(&_camera.camera_info.framebuffer[0]),(uint32_t)(&_camera.camera_info.framebuffer[CAMERA_BUF_SIZE / 2]),CAMERA_BUF_SIZE / 2);//开启双缓冲
//    __HAL_DMA_ENABLE_IT(&_camera.hdma_dcmi,DMA_IT_TC);    //开启传输完成中断
//    /* DMA1_Stream1_IRQn interrupt configuration */
//    HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 2);
//    HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
//    /* DMAMUX1_OVR_IRQn interrupt configuration */
//    HAL_NVIC_SetPriority(DMAMUX2_OVR_IRQn, 0, 0);
//    HAL_NVIC_EnableIRQ(DMAMUX2_OVR_IRQn);


    //设置全尺寸缩放
    uint16_t temp = 0;
    uint16_t hsize = 1600 / 4;
    uint16_t vsize  = 1200 / 4;
    camera_write_reg(0XFF,0X00);
    camera_write_reg(0XE0,0X04);
    camera_write_reg(0X51,hsize & 0XFF);       //设置H_SIZE的低八位
    camera_write_reg(0X52,vsize & 0XFF);       //设置V_SIZE的低八位
    camera_write_reg(0X53,0&0XFF);        //设置offx的低八位
    camera_write_reg(0X54,0&0XFF);        //设置offy的低八位
    temp = (vsize >> 1) & 0X80;
    temp |= (0 >> 4) & 0X70;
    temp |= (hsize >> 5) & 0X08;
    temp |= (0 >> 8) & 0X07;
    camera_write_reg(0X55,temp);             //设置H_SIZE/V_SIZE/OFFX,OFFY的高位
    camera_write_reg(0X57,(hsize >> 2) & 0X80);  //设置H_SIZE/V_SIZE/OFFX,OFFY的高位
    camera_write_reg(0XE0,0X00);
    //设置输出大小
    camera_set_outsize(RT_ALIGN(LCD_WIDTH,4), RT_ALIGN(CAMERA_HEIGHT,4));

    __HAL_DMA_ENABLE(&_camera.hdma_dcmi); //使能DMA
    DCMI->CR|=DCMI_CR_CAPTURE;          //DCMI捕获使能
    return RT_EOK;

_exit:
    return -RT_ERROR;
}

void DCMI_IRQHandler(void)
{
    HAL_DCMI_IRQHandler(&_camera.dcmi);
//    LOG_D("DCMI IRQ");
}
//DCMI,启动传输
void DCMI_Start(void)
{
    __HAL_DMA_ENABLE(&_camera.hdma_dcmi); //使能DMA
    DCMI->CR |= DCMI_CR_CAPTURE;          //DCMI捕获使能
}

//DCMI,关闭传输
void DCMI_Stop(void)
{
    DCMI->CR &= ~(DCMI_CR_CAPTURE);       //关闭捕获
    while(DCMI->CR & 0X01);               //等待传输完成
    __HAL_DMA_DISABLE(&_camera.hdma_dcmi);//关闭DMA
}
//捕获到一帧图像处理函数
//hdcmi:DCMI句柄
void HAL_DCMI_FrameEventCallback(DCMI_HandleTypeDef *hdcmi)
{
    //jpeg_data_process();//jpeg数据处理
    //重新使能帧中断,因为HAL_DCMI_IRQHandler()函数会关闭帧中断
    __HAL_DCMI_ENABLE_IT(&_camera.dcmi,DCMI_IT_FRAME);
    LOG_D("DCMI FrameIRQ");
    rt_sem_release(&_camera.camera_sem);
}

//DMA2数据流1中断服务函数
void DMA1_Stream1_IRQHandler(void)
{
    if(__HAL_DMA_GET_FLAG(&_camera.hdma_dcmi,DMA_FLAG_TCIF1_5) != RESET)//DMA传输完成
    {
        __HAL_DMA_CLEAR_FLAG(&_camera.hdma_dcmi,DMA_FLAG_TCIF1_5);//清除DMA传输完成中断标志位
//        CAMERA_BUF_SIZE / 3
//        if(DMA2_Stream1->CR&(1<<19))netcam_fifo_write(netcam_line_buf0);    //netcam_line_buf0写入FIFO
//            else netcam_fifo_write(netcam_line_buf1);                           //netcam_line_buf1写入FIFO
    }
    LOG_D("DMA IRQ");
}

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device_ops camera_ops =
{
    drv_camera_init,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    RT_NULL,
    drv_camera_control
};
#endif
int drv_camera_hw_init(void)
{
    rt_err_t result = RT_EOK;
    struct rt_device *device = &_camera.parent;
    
    _camera.i2c_device = (struct rt_i2c_bus_device *)rt_device_find(CAMERA_IIC_BUS_NAME);

    RT_ASSERT(_camera.i2c_device != RT_NULL);

    /* init camera_lock mutex */
    result = rt_mutex_init(&_camera.camera_lock, "camera_lock", RT_IPC_FLAG_FIFO);
    if (result != RT_EOK)
    {
        LOG_E("init mutex failed!\n");
        result = -RT_ENOMEM;
        goto __exit;
    }
    /* init camera_sem sem */
    result = rt_sem_init(&_camera.camera_sem, "camera_sem", 0, RT_IPC_FLAG_FIFO);
    if (result != RT_EOK)
    {
        LOG_E("init sem failed!\n");
        result = -RT_ENOMEM;
        goto __exit;
    }

    /* config CAMERA dev info */
    _camera.camera_info.height = CAMERA_HEIGHT;
    _camera.camera_info.width = CAMERA_WIDTH;
    _camera.camera_info.bits_per_pixel = CAMERA_BITS_PER_PIXEL;
    _camera.camera_info.pixel_format = CAMERA_PIXEL_FORMAT;

    /* malloc memory for Buffering */
    _camera.camera_info.framebuffer = rt_malloc(CAMERA_BUF_SIZE);
    if (_camera.camera_info.framebuffer == RT_NULL)
    {
        LOG_E("init frame buffer failed!\n");
        result = -RT_ENOMEM;
        goto __exit;
    }

    /* memset buff to 0x00 */
    rt_memset(_camera.camera_info.framebuffer, 0x00, CAMERA_BUF_SIZE);

    device->type    = RT_Device_Class_Graphic;
#ifdef RT_USING_DEVICE_OPS
    device->ops     = &camera_ops;
#else
    device->init    = drv_camera_init;
    device->read    = drv_camera_read;
    device->control = drv_camera_control;
#endif

    /* register camera device */
    rt_device_register(device, "camera", RT_DEVICE_FLAG_RDWR);
#ifdef CAMERA_RST_GPIO_NUM
    rst_camera();
#endif
#ifdef CAMERA_EN_GPIO_NUM
    en_camera();
#endif
#ifdef CAMERA_PWND_GPIO_NUM
    en_camera_pwnd();
#endif
    /* init stm32 DCMI */
    if (stm32_camera_init(&_camera) != RT_EOK)
    {
        LOG_E("camera init failed");
        result = -RT_ERROR;
        goto __exit;
    }
    else {
        LOG_D("camera init success");
    }

__exit:
    if (result != RT_EOK)
    {
        rt_mutex_detach(&_camera.camera_lock);
        rt_sem_detach(&_camera.camera_sem);

        if (_camera.camera_info.framebuffer)
        {
            rt_free(_camera.camera_info.framebuffer);
        }
    }
    return result;
}
INIT_COMPONENT_EXPORT(drv_camera_hw_init);

#ifdef BSP_USING_LCD
void lcd_camera_thread_entry(void *parameter)
{
    struct rt_device_graphic_info info;
    struct rt_device_rect_info rect_info = {
                            .x = 0,
                            .y = 0,
                            .width = LCD_WIDTH,
                            .height = LCD_HEIGHT
                    };
    rt_device_t dev = rt_device_find("camera");
    rt_device_t lcd_device = rt_device_find("lcd");

    RT_ASSERT(dev != RT_NULL && lcd_device != RT_NULL);

    rt_device_open(lcd_device, RT_DEVICE_OFLAG_RDWR);
    rt_device_control(lcd_device, RTGRAPHIC_CTRL_GET_INFO, &info);

    for(;;)
    {
        if(rt_sem_take(&_camera.camera_sem, RT_WAITING_FOREVER) == RT_EOK)
        {
#ifdef BSP_USING_LVGL
            DCMI_Stop();
            extern void reflash_camera(void);
            reflash_camera();
            DCMI_Start();
#else
            DCMI_Stop();
            rt_memcpy(info.framebuffer, &_camera.camera_info.framebuffer[0], LCD_WIDTH * LCD_HEIGHT * LCD_BITS_PER_PIXEL / 8);
            rt_device_control(lcd_device, RTGRAPHIC_CTRL_RECT_UPDATE, &rect_info);
            DCMI_Start();
#endif
        }
    }
}
int lcd_app_init(void)
{
    rt_thread_t tid = rt_thread_create("lcd", lcd_camera_thread_entry, RT_NULL, 4096, 10, 10);
    if(tid != RT_NULL)
    {
        rt_thread_startup(tid);
        return RT_EOK;
    }
    else
    {
        return -RT_ENOMEM;
    }
}
INIT_APP_EXPORT(lcd_app_init);
#endif /* BSP_USING_LCD */

#endif /* BSP_USING_CAMERA */
